library(tidyverse)
library(knitr)
library(sjPlot)
library(gridExtra)
library(lme4)
library(emmeans)
library(car) # for vif
library(bbmle) # for AICtab
library(broom) # for glance
theme_set(ggthemes::theme_few())
Summary
Mixed modeling with all relevant variables predicting accuracy
From the preregistration, the mixed model was specified thusly:
correct ~ delay * age +
task_experience + cup_distance + board_size + trial +
(1 + delay + trial | site/subject/block/hiding_location ) +
(1 + task_experience + cup_distance + board_size + trial + delay | species)
In the dataframe, subject_site = subject, and norm_age should be used for age.
Model as pre-registered has too many random effects
Error: number of observations (=6246) < number of random effects (=10608) for term (1 + delay + trial | hiding_location:(block:(subject_site:site))); the random-effects parameters are probably unidentifiable
Pruning random effects in the following order (from preregistration):
- Remove correlations between random effects
- Remove random slopes (in the following order)
species
hiding_location
block
subject
Model only converges once we take out hiding_location. After doing so, the other random effects (correlation, site, species) can be put back in.
The model below converges. Model output is saved in 06_mp_model_v2.rds
correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + delay + trial | site/subject_site) +
(1 + task_experience + cup_distance + board_size + trial + delay | species)
Reduced model
After pruning random effects with little variability and removing board_size, which covaried with cup_distance, the reduced model has the following structure. It is saved in 06_mp_3_model3_v2.rds
correct ~ delay * norm_age +
task_experience + cup_distance + trial +
(1 + delay | site/subject_site) +
(1 + delay | species)
Data prep
Data import
mp_data <- read.csv("../data/merged_data/01_manyprimates_pilot_merged_data_v2.csv")
Prepare code for pre-registered mixed modeling
- center
cup_distance, board_size and trial
- filter out spider monkey. Only one data point so far, therefore this is not worth including to explode the number of random effects
model.data <- mp_data %>%
filter(species != "black_faced_spider_monkey") %>%
mutate_at(vars(cup_distance, board_size, trial), funs(scale(.)[, 1])) %>%
mutate(hiding_location = factor(hiding_location),
delay = fct_relevel(delay, "short"))
Model 1
The model takes a while to run. Run next line to load model output from previous run with structure below.
mm.1 <- glmer(correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + delay + trial | site/subject_site/block) +
(1 + task_experience + cup_distance + board_size + trial + delay | species)
, data = model.data
, family = binomial
, control = glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 2e5))
)
saveRDS(mm.1, "06_mp_model_v2.rds")
Some diagnostics
- examining Cholesky decomposition
theta <- getME(mm.1, "theta")
diag.element <- getME(mm.1, "lower") == 0
any(theta[diag.element] < 1e-5)
Model summary
Confirm model structure
# mm.1@call
formula(mm.1)
correct ~ delay * norm_age + task_experience + cup_distance +
board_size + trial + (1 + delay + trial | site/subject_site) +
(1 + task_experience + cup_distance + board_size + trial +
delay | species)
glance(mm.1) %>% kable(digits = 2)
Random effects
VarCorr(mm.1) %>% print(formatter = fmt, digits = 3) # comp = c("Variance", "Std.Dev.")
Groups Name Std.Dev. Corr
subject_site:site (Intercept) 0.862
delaylong 0.646 -0.91
delaymedium 0.528 -0.85 0.99
trial 0.093 -0.31 0.61 0.71
site (Intercept) 0.917
delaylong 0.512 -1.00
delaymedium 0.627 -1.00 1.00
trial 0.084 1.00 -1.00 -1.00
species (Intercept) 0.835
task_experienceyes 0.173 -1.00
cup_distance 0.012 1.00 -1.00
board_size 0.232 -1.00 1.00 -1.00
trial 0.021 -1.00 1.00 -1.00 1.00
delaylong 0.494 -1.00 1.00 -1.00 1.00 1.00
delaymedium 0.436 -1.00 1.00 -1.00 1.00 1.00
1.00
Fixed effects
CIs
mm.1.ci <- confint(mm.1, method = "Wald") %>% # bootstrap these later
as.data.frame %>%
rownames_to_column %>%
filter(complete.cases(.)) %>%
rename(LL = `2.5 %`, UL = `97.5 %`) %>%
mutate(OR_LL = exp(LL), OR_UL = exp(UL))
coef(summary(mm.1)) %>%
as.data.frame %>%
rownames_to_column() %>%
mutate(OR = exp(Estimate)) %>%
left_join(mm.1.ci, by = "rowname") %>%
select(rowname, OR, OR_LL, OR_UL, Estimate, LL, UL, everything()) %>%
kable(digits = 3)
corr <- cov2cor(vcov(mm.1)) %>% as.matrix %>% round(2)
corr[upper.tri(corr, diag = T)] <- ""
colnames(corr) <- 1:10
rownames(corr) <- str_c(1:10, " ", rownames(corr))
corr %>% as.data.frame %>% select(-10) %>% rownames_to_column
Pairwise contrasts for delay
based on estimated marginal means
Note. This wasn’t in the preregistration.
emmeans(mm.1, pairwise ~ delay, type = "response")$contrasts
contrast odds.ratio SE df z.ratio p.value
short / long 3.5160746 0.92828519 Inf 4.762 <.0001
short / medium 2.7103806 0.75716209 Inf 3.569 0.0010
long / medium 0.7708541 0.07335892 Inf -2.735 0.0172
Results are averaged over the levels of: task_experience
P value adjustment: tukey method for comparing a family of 3 estimates
Tests are performed on the log odds ratio scale
Model 1 plots
Fixed effects
plot_model(mm.1, title = "Fixed Effects", order.terms = c(7, 4, 3:1, 9:8, 5, 6),
width = .3, show.values = T, value.size = 2.5, value.offset = .3) +
geom_hline(yintercept = 1, lty = 2) +
ylim(0, 3)
Random effects
Subject/Site
ranef.plots[[1]]
Site

Species

Pruning the model
- remove
trial random slopes within species as the estimates in the previous models were essentially 0
correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + delay + trial | site/subject_site ) +
(1 + task_experience + cup_distance + board_size + delay | species)
Check colinearity in the previous model
vif(col.mm1)
GVIF Df GVIF^(1/(2*Df))
delay 1.007876 2 1.001963
norm_age 1.072757 1 1.035740
task_experience 1.056383 1 1.027805
cup_distance 1.323990 1 1.150648
board_size 1.296698 1 1.138726
trial 1.001067 1 1.000533
No signs of high colinaearity.
Check levels of random effects
Check how many different levels there are within each random effect
overview$summary
$`delay_within_species (factor)`
3
11
$`task_experience_within_species (factor)`
1 2
9 2
$`board_size_within_species (covariate)`
1 2 4
5 5 1
$`cup_distance_within_species (covariate)`
1 2 4
6 4 1
$`trial_within_species (covariate)`
36
11
This suggests that, within species, random slopes for task_experience does not make much sense as most species have only 1 level. Same is true for cup_distance and board_size. Indeed, the model summary and random effects plot for species confirm that there is little variability in these estimates (they’re close to zero). Therefore they are removed.
correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + delay + trial | site/subject_site ) +
(1 + delay | species)
Model 2
The model takes a while to run. Run next line to load model output from previous run with structure below.
mm.2 <- readRDS("06_2_mp_model2_v2.rds")
# mm.2.ci <- readRDS("06_2_mp_model2_ci_v2.rds")
mm.2 <- glmer(correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + trial + delay | site/subject_site) +
(1 + delay | species)
, data = model.data
, family = binomial
, control = glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 2e5))
)
saveRDS(mm.2, "06_2_mp_model2_v2.rds")
Model 3
Remove trial from the random slopes for subject/site as it’s near zero both in mm.1 and even more so in mm.2
VarCorr(mm.2) %>% print(comp = c("Variance", "Std.Dev."), formatter = fmt, digits = 3)
Groups Name Variance Std.Dev. Corr
subject_site:site (Intercept) 0.74 0.86
trial 0.009 0.093 -0.29
delaylong 0.417 0.646 -0.91 0.60
delaymedium 0.279 0.528 -0.85 0.69 0.99
site (Intercept) 1.052 1.026
trial 0.008 0.089 0.88
delaylong 0.349 0.591 -1.00 -0.88
delaymedium 0.471 0.686 -1.00 -0.89 1.00
species (Intercept) 0.576 0.759
delaylong 0.246 0.496 -1.00
delaymedium 0.183 0.428 -1.00 1.00


The model takes a while to run. Run next line to load model output from previous run with structure below.
mm.3 <- readRDS("06_3_mp_model3_v2.rds")
mm.3 <- glmer(correct ~ delay * norm_age +
task_experience + cup_distance + board_size + trial +
(1 + delay | site/subject_site) +
(1 + delay | species)
, data = model.data
, family = binomial
, control = glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 2e5))
)
saveRDS(mm.3, "06_3_mp_model3_v2.rds")
Model summary
Confirm model structure
formula(mm.3)
correct ~ delay * norm_age + task_experience + cup_distance +
board_size + trial + (1 + delay | site/subject_site) + (1 +
delay | species)
glance(mm.3) %>% kable(digits = 2)
LRT
drop1(mm.3, test = 'Chisq')
Single term deletions
Model:
correct ~ delay * norm_age + task_experience + cup_distance +
trial + (1 + delay | site/subject_site) + (1 + delay | species)
Df AIC LRT Pr(Chi)
<none> 6836.2
task_experience 1 6834.4 0.1483 0.7001
cup_distance 1 6854.5 20.3087 6.59e-06 ***
trial 1 6835.0 0.7541 0.3852
delay:norm_age 2 6832.6 0.3836 0.8255
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Random effects
VarCorr(mm.3) %>% print(comp = c("Variance", "Std.Dev."), formatter = fmt, digits = 3)
Fixed effects
CIs
# this is not currently run
source("boot_glmm.r")
mm.3.ci <- boot.glmm.pred(model.res = mm.3, excl.warnings = F, nboots = 1000,
para = F, resol = 100, level = 0.95, use = NULL,
circ.var.name = NULL, circ.var = NULL, use.u = F,
n.cores = c("all-1", "all"), save.path = NULL)
saveRDS(mm.3.ci, "06_3_mp_model3_ci_v2.rds")
mm.3.ci <- confint(mm.3, method = "Wald") %>% # bootstrap these later
as.data.frame %>%
rownames_to_column %>%
filter(complete.cases(.)) %>%
rename(LL = `2.5 %`, UL = `97.5 %`) %>%
mutate(OR_LL = exp(LL), OR_UL = exp(UL))
coef(summary(mm.3)) %>%
as.data.frame %>%
rownames_to_column() %>%
mutate(OR = exp(Estimate)) %>%
left_join(mm.3.ci, by = "rowname") %>%
select(rowname, OR, OR_LL, OR_UL, Estimate, LL, UL, everything()) %>%
kable(digits = 3)
Pairwise contrasts for delay
based on estimated marginal means
(emm <- emmeans(mm.3, pairwise ~ delay)$contrasts)
contrast estimate SE df z.ratio p.value
short - long 1.4257372 0.2800475 Inf 5.091 <.0001
short - medium 1.1604769 0.2810297 Inf 4.129 0.0001
long - medium -0.2652602 0.0865917 Inf -3.063 0.0062
Results are averaged over the levels of: task_experience
Results are given on the log odds ratio (not the response) scale.
P value adjustment: tukey method for comparing a family of 3 estimates
confint(emm)
contrast estimate SE df asymp.LCL asymp.UCL
short - long 1.4257372 0.2800475 Inf 0.7693896 2.08208469
short - medium 1.1604769 0.2810297 Inf 0.5018275 1.81912636
long - medium -0.2652602 0.0865917 Inf -0.4682053 -0.06231523
Results are averaged over the levels of: task_experience
Results are given on the log odds ratio (not the response) scale.
Confidence level used: 0.95
Conf-level adjustment: tukey method for comparing a family of 3 estimates
(emm.or <- emmeans(mm.3, pairwise ~ delay, type = "response")$contrasts)
contrast odds.ratio SE df z.ratio p.value
short / long 4.1609240 1.16525642 Inf 5.091 <.0001
short / medium 3.1914550 0.89689360 Inf 4.129 0.0001
long / medium 0.7670063 0.06641638 Inf -3.063 0.0062
Results are averaged over the levels of: task_experience
P value adjustment: tukey method for comparing a family of 3 estimates
Tests are performed on the log odds ratio scale
confint(emm.or)
Model 3 plots
Fixed effects

ggsave("../graphs/05_forestplot.png", fe.3, width = 3, height = 2.5, scale = 2)
Random effects
Subject/Site
ranef.plots2[[1]]
Species

Model 4
- further remove subject/site random effects to look at species differences (from preregistration)
Random effects
plot.data <- get_model_data(mm.4, type = "re", show.values = T) %>%
left_join(phylo, by = c("term" = "species")) %>%
rename(species = species_formatted) %>%
mutate(
facet = fct_rev(facet),
facet = fct_recode(facet, "Intercept" = "species (Intercept)",
"Delay (short vs. long)" = "delaylong",
"Delay (short vs. medium)" = "delaymedium")
)
Column `term`/`species` joining factor and character vector, coercing into character vector


Model comparison
We’re looking for the lowest AIC(c) as the model with the ‘best fit’ with a reasonable number of parameters. (Too many are penalized by AIC as one way to address overfitting.)
Indeed, the reduced model seems to do a better job of striking that balance between fitting the data with fewer parameters.
AICctab(mm.1, mm.2, mm.3, mm.4, logLik = T, weights = T)
dLogLik dAICc df weight
mm.3 109.9 0.0 28 0.9982
mm.2 111.6 12.7 36 0.0018
mm.1 112.9 54.9 58 <0.001
mm.4 0.0 193.6 15 <0.001
anova(mm.1, mm.2, mm.3, mm.4)
Data: model.data
Models:
mm.4: correct ~ delay * norm_age + task_experience + cup_distance +
mm.4: trial + (1 + delay | species)
mm.3: correct ~ delay * norm_age + task_experience + cup_distance +
mm.3: board_size + trial + (1 + delay | site/subject_site) + (1 +
mm.3: delay | species)
mm.2: correct ~ delay * norm_age + task_experience + cup_distance +
mm.2: board_size + trial + (1 + trial + delay | site/subject_site) +
mm.2: (1 + delay | species)
mm.1: correct ~ delay * norm_age + task_experience + cup_distance +
mm.1: board_size + trial + (1 + delay + trial | site/subject_site) +
mm.1: (1 + task_experience + cup_distance + board_size + trial +
mm.1: delay | species)
Df AIC BIC logLik deviance Chisq Chi Df Pr(>Chisq)
mm.4 15 7032.3 7133.4 -3501.1 7002.3
mm.3 28 6838.5 7027.2 -3391.3 6782.5 219.7490 13 <2e-16 ***
mm.2 36 6851.0 7093.6 -3389.5 6779.0 3.5119 8 0.8983
mm.1 58 6892.6 7283.5 -3388.3 6776.6 2.4403 22 1.0000
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Difference in regression coefficients
Difference
coef2 - coef1
delaylong delaymedium cup_distance
-0.16840056 -0.16341754 -0.06131301
Difference in odds ratios
exp(coef2) - exp(coef1)
delaylong delaymedium cup_distance
-0.04407273 -0.05561161 -0.09242183
LS0tCnRpdGxlOiAiTWFueVByaW1hdGVzIHBpbG90IG1peGVkIG1vZGVsaW5nIgphdXRob3I6ICJEcmV3IEFsdHNjaHVsIgpkYXRlOiAiMTggT2N0IDIwMTgiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY3NzOiBzdHlsZS5jc3MKICAgIHRoZW1lOiBwYXBlcgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHNqUGxvdCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobG1lNCkKbGlicmFyeShlbW1lYW5zKQpsaWJyYXJ5KGNhcikgIyBmb3IgdmlmCmxpYnJhcnkoYmJtbGUpICMgZm9yIEFJQ3RhYgpsaWJyYXJ5KGJyb29tKSAjIGZvciBnbGFuY2UKCnRoZW1lX3NldChnZ3RoZW1lczo6dGhlbWVfZmV3KCkpCmBgYAoKIyBTdW1tYXJ5CgpNaXhlZCBtb2RlbGluZyB3aXRoIGFsbCByZWxldmFudCB2YXJpYWJsZXMgcHJlZGljdGluZyBhY2N1cmFjeQoKRnJvbSB0aGUgcHJlcmVnaXN0cmF0aW9uLCB0aGUgbWl4ZWQgbW9kZWwgd2FzIHNwZWNpZmllZCB0aHVzbHk6CgpgYGAKY29ycmVjdCB+IGRlbGF5ICogYWdlICsgCiAgICAgICAgICB0YXNrX2V4cGVyaWVuY2UgKyBjdXBfZGlzdGFuY2UgKyBib2FyZF9zaXplICsgdHJpYWwgKwogICAgICAgICAgKDEgKyBkZWxheSArIHRyaWFsIHwgc2l0ZS9zdWJqZWN0L2Jsb2NrL2hpZGluZ19sb2NhdGlvbiApICsgCiAgICAgICAgICAoMSArIHRhc2tfZXhwZXJpZW5jZSArIGN1cF9kaXN0YW5jZSArIGJvYXJkX3NpemUgKyB0cmlhbCArIGRlbGF5IHwgc3BlY2llcykKYGBgCgpJbiB0aGUgZGF0YWZyYW1lLCAKYHN1YmplY3Rfc2l0ZSA9IHN1YmplY3RgLAphbmQgYG5vcm1fYWdlYCBzaG91bGQgYmUgdXNlZCBmb3IgYGFnZWAuCgpNb2RlbCBhcyBwcmUtcmVnaXN0ZXJlZCBoYXMgdG9vIG1hbnkgcmFuZG9tIGVmZmVjdHMKCmBgYApFcnJvcjogbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAoPTYyNDYpIDwgbnVtYmVyIG9mIHJhbmRvbSBlZmZlY3RzICg9MTA2MDgpIGZvciB0ZXJtICgxICsgZGVsYXkgKyB0cmlhbCB8IGhpZGluZ19sb2NhdGlvbjooYmxvY2s6KHN1YmplY3Rfc2l0ZTpzaXRlKSkpOyB0aGUgcmFuZG9tLWVmZmVjdHMgcGFyYW1ldGVycyBhcmUgcHJvYmFibHkgdW5pZGVudGlmaWFibGUKYGBgCgpQcnVuaW5nIHJhbmRvbSBlZmZlY3RzIGluIHRoZSBmb2xsb3dpbmcgb3JkZXIgKGZyb20gcHJlcmVnaXN0cmF0aW9uKTogCgo+IC0gUmVtb3ZlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHJhbmRvbSBlZmZlY3RzCj4gLSBSZW1vdmUgcmFuZG9tIHNsb3BlcyAoaW4gdGhlIGZvbGxvd2luZyBvcmRlcikKPiAgICAgLSBgc3BlY2llc2AKPiAgICAgLSBgaGlkaW5nX2xvY2F0aW9uYAo+ICAgICAtIGBibG9ja2AKPiAgICAgLSBgc3ViamVjdGAKCk1vZGVsIG9ubHkgY29udmVyZ2VzIG9uY2Ugd2UgdGFrZSBvdXQgYGhpZGluZ19sb2NhdGlvbmAuIEFmdGVyIGRvaW5nIHNvLCB0aGUgb3RoZXIgcmFuZG9tIGVmZmVjdHMgKGNvcnJlbGF0aW9uLCBzaXRlLCBzcGVjaWVzKSBjYW4gYmUgcHV0IGJhY2sgaW4uCgpUaGUgbW9kZWwgYmVsb3cgY29udmVyZ2VzLiBNb2RlbCBvdXRwdXQgaXMgc2F2ZWQgaW4gYDA2X21wX21vZGVsX3YyLnJkc2AKCmBgYApjb3JyZWN0IH4gZGVsYXkgKiBub3JtX2FnZSArIAogICAgICAgICAgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgYm9hcmRfc2l6ZSArIHRyaWFsICsgCiAgICAgICAgICAoMSArIGRlbGF5ICsgdHJpYWwgfCBzaXRlL3N1YmplY3Rfc2l0ZSkgKyAKICAgICAgICAgICgxICsgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgYm9hcmRfc2l6ZSArIHRyaWFsICsgZGVsYXkgfCBzcGVjaWVzKQpgYGAKCiMjIFJlZHVjZWQgbW9kZWwKCkFmdGVyIHBydW5pbmcgcmFuZG9tIGVmZmVjdHMgd2l0aCBsaXR0bGUgdmFyaWFiaWxpdHkgYW5kIHJlbW92aW5nIGBib2FyZF9zaXplYCwgd2hpY2ggY292YXJpZWQgd2l0aCBgY3VwX2Rpc3RhbmNlYCwgdGhlIHJlZHVjZWQgbW9kZWwgaGFzIHRoZSBmb2xsb3dpbmcgc3RydWN0dXJlLiBJdCBpcyBzYXZlZCBpbiBgMDZfbXBfM19tb2RlbDNfdjIucmRzYAoKYGBgCmNvcnJlY3QgfiBkZWxheSAqIG5vcm1fYWdlICsgCiAgICAgICAgICB0YXNrX2V4cGVyaWVuY2UgKyBjdXBfZGlzdGFuY2UgKyB0cmlhbCArIAogICAgICAgICAgKDEgKyBkZWxheSB8IHNpdGUvc3ViamVjdF9zaXRlKSArIAogICAgICAgICAgKDEgKyBkZWxheSB8IHNwZWNpZXMpCmBgYAoKIVtdKC4uL2dyYXBocy8wNV9mb3Jlc3RwbG90LnBuZykKCioqKgoKIyBEYXRhIHByZXAKCkRhdGEgaW1wb3J0CgpgYGB7ciBsb2FkaW5nIGRhdGF9Cm1wX2RhdGEgPC0gcmVhZC5jc3YoIi4uL2RhdGEvbWVyZ2VkX2RhdGEvMDFfbWFueXByaW1hdGVzX3BpbG90X21lcmdlZF9kYXRhX3YyLmNzdiIpCmBgYAoKUHJlcGFyZSBjb2RlIGZvciBwcmUtcmVnaXN0ZXJlZCBtaXhlZCBtb2RlbGluZwoKLSBjZW50ZXIgYGN1cF9kaXN0YW5jZWAsIGBib2FyZF9zaXplYCBhbmQgYHRyaWFsYAotIGZpbHRlciBvdXQgc3BpZGVyIG1vbmtleS4gT25seSBvbmUgZGF0YSBwb2ludCBzbyBmYXIsIHRoZXJlZm9yZSB0aGlzIGlzIG5vdCB3b3J0aCBpbmNsdWRpbmcgdG8gZXhwbG9kZSB0aGUgbnVtYmVyIG9mIHJhbmRvbSBlZmZlY3RzCgpgYGB7cn0KbW9kZWwuZGF0YSA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzICE9ICJibGFja19mYWNlZF9zcGlkZXJfbW9ua2V5IikgJT4lCiAgbXV0YXRlX2F0KHZhcnMoY3VwX2Rpc3RhbmNlLCBib2FyZF9zaXplLCB0cmlhbCksIGZ1bnMoc2NhbGUoLilbLCAxXSkpICU+JQogIG11dGF0ZShoaWRpbmdfbG9jYXRpb24gPSBmYWN0b3IoaGlkaW5nX2xvY2F0aW9uKSwKICAgICAgICAgZGVsYXkgPSBmY3RfcmVsZXZlbChkZWxheSwgInNob3J0IikpCmBgYAoKIyBNb2RlbCAxCgpUaGUgbW9kZWwgdGFrZXMgYSB3aGlsZSB0byBydW4uIFJ1biBuZXh0IGxpbmUgdG8gbG9hZCBtb2RlbCBvdXRwdXQgZnJvbSBwcmV2aW91cyBydW4gd2l0aCBzdHJ1Y3R1cmUgYmVsb3cuCgpgYGB7cn0KIyBtbS4xIDwtIHJlYWRSRFMoIjA2X21wX21vZGVsLnJkcyIpCm1tLjEgPC0gcmVhZFJEUygiMDZfbXBfbW9kZWxfdjIucmRzIikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbW0uMSA8LSBnbG1lcihjb3JyZWN0IH4gZGVsYXkgKiBub3JtX2FnZSArCiAgICAgICAgICAgICAgIHRhc2tfZXhwZXJpZW5jZSArIGN1cF9kaXN0YW5jZSArIGJvYXJkX3NpemUgKyB0cmlhbCArCiAgICAgICAgICAgICAgICgxICsgZGVsYXkgKyB0cmlhbCB8IHNpdGUvc3ViamVjdF9zaXRlL2Jsb2NrKSArCiAgICAgICAgICAgICAgICgxICsgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgYm9hcmRfc2l6ZSArIHRyaWFsICsgZGVsYXkgfCBzcGVjaWVzKQogICAgICAgICAgICAgLCBkYXRhID0gbW9kZWwuZGF0YQogICAgICAgICAgICAgLCBmYW1pbHkgPSBiaW5vbWlhbAogICAgICAgICAgICAgLCBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiLCBvcHRDdHJsID0gbGlzdChtYXhmdW4gPSAyZTUpKQogICAgICAgICAgICAgKQoKc2F2ZVJEUyhtbS4xLCAiMDZfbXBfbW9kZWxfdjIucmRzIikKYGBgCgpTb21lIGRpYWdub3N0aWNzCgotIGV4YW1pbmluZyBDaG9sZXNreSBkZWNvbXBvc2l0aW9uCgpgYGB7cn0KdGhldGEgPC0gZ2V0TUUobW0uMSwgInRoZXRhIikKZGlhZy5lbGVtZW50IDwtIGdldE1FKG1tLjEsICJsb3dlciIpID09IDAKYW55KHRoZXRhW2RpYWcuZWxlbWVudF0gPCAxZS01KQpgYGAKCiMjIE1vZGVsIHN1bW1hcnkKCkNvbmZpcm0gbW9kZWwgc3RydWN0dXJlCgpgYGB7cn0KIyBtbS4xQGNhbGwKZm9ybXVsYShtbS4xKQpgYGAKCmBgYHtyLCByZXN1bHRzPSJhc2lzIn0KZ2xhbmNlKG1tLjEpICU+JSBrYWJsZShkaWdpdHMgPSAyKQpgYGAKCiMjIFJhbmRvbSBlZmZlY3RzCgpgYGB7cn0KZm10IDwtIGZ1bmN0aW9uKG51bSwgZGlnaXRzKSByZXR1cm4ocm91bmQobnVtLCBkaWdpdHMpKQpWYXJDb3JyKG1tLjEpICU+JSBwcmludChmb3JtYXR0ZXIgPSBmbXQsIGRpZ2l0cyA9IDMpICMgY29tcCA9IGMoIlZhcmlhbmNlIiwgIlN0ZC5EZXYuIikKYGBgCgojIyBGaXhlZCBlZmZlY3RzCgpDSXMKCmBgYHtyfQptbS4xLmNpIDwtIGNvbmZpbnQobW0uMSwgbWV0aG9kID0gIldhbGQiKSAlPiUgIyBib290c3RyYXAgdGhlc2UgbGF0ZXIKICBhcy5kYXRhLmZyYW1lICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbiAlPiUKICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpICU+JQogIHJlbmFtZShMTCA9IGAyLjUgJWAsIFVMID0gYDk3LjUgJWApICU+JQogIG11dGF0ZShPUl9MTCA9IGV4cChMTCksIE9SX1VMID0gZXhwKFVMKSkKYGBgCgpgYGB7ciwgcmVzdWx0cz0iYXNpcyJ9CmNvZWYoc3VtbWFyeShtbS4xKSkgJT4lCiAgYXMuZGF0YS5mcmFtZSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBtdXRhdGUoT1IgPSBleHAoRXN0aW1hdGUpKSAlPiUKICBsZWZ0X2pvaW4obW0uMS5jaSwgYnkgPSAicm93bmFtZSIpICU+JQogIHNlbGVjdChyb3duYW1lLCBPUiwgT1JfTEwsIE9SX1VMLCBFc3RpbWF0ZSwgTEwsIFVMLCBldmVyeXRoaW5nKCkpICU+JQogIGthYmxlKGRpZ2l0cyA9IDMpCmBgYAoKPCEtLSAjIyBDb3JyZWxhdGlvbiBvZiBGaXhlZCBFZmZlY3RzIC0tPgoKYGBge3IsIGV2YWw9RkFMU0UsIHJlc3VsdHM9ImFzaXMifQpjb3JyIDwtIGNvdjJjb3IodmNvdihtbS4xKSkgJT4lIGFzLm1hdHJpeCAlPiUgcm91bmQoMikKY29yclt1cHBlci50cmkoY29yciwgZGlhZyA9IFQpXSA8LSAiIgpjb2xuYW1lcyhjb3JyKSA8LSAxOjEwCnJvd25hbWVzKGNvcnIpIDwtIHN0cl9jKDE6MTAsICIgIiwgcm93bmFtZXMoY29ycikpCgpjb3JyICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZWxlY3QoLTEwKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uCmBgYAoKIyMgUGFpcndpc2UgY29udHJhc3RzIGZvciBkZWxheQoKYmFzZWQgb24gZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zCgoqTm90ZS4gVGhpcyB3YXNuJ3QgaW4gdGhlIHByZXJlZ2lzdHJhdGlvbi4qCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KZW1tZWFucyhtbS4xLCBwYWlyd2lzZSB+IGRlbGF5LCB0eXBlID0gInJlc3BvbnNlIikkY29udHJhc3RzCmBgYAoKIyBNb2RlbCAxIHBsb3RzCgojIyBGaXhlZCBlZmZlY3RzCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9Mi41LCBtZXNzYWdlPUZBTFNFfQpwbG90X21vZGVsKG1tLjEsIHRpdGxlID0gIkZpeGVkIEVmZmVjdHMiLCBvcmRlci50ZXJtcyA9IGMoNywgNCwgMzoxLCA5OjgsIDUsIDYpLAogICAgICAgICAgIHdpZHRoID0gLjMsIHNob3cudmFsdWVzID0gVCwgdmFsdWUuc2l6ZSA9IDIuNSwgdmFsdWUub2Zmc2V0ID0gLjMpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyKSArCiAgeWxpbSgwLCAzKQpgYGAKCiMjIFJhbmRvbSBlZmZlY3RzCgpgYGB7cn0KcmFuZWYucGxvdHMgPC0gcGxvdF9tb2RlbChtbS4xLCB0eXBlID0gInJlIiwgc29ydC5lc3QgPSAiKEludGVyY2VwdCkiKQpgYGAKCiMjIyBTdWJqZWN0L1NpdGUKCmBgYHtyIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xMH0KcmFuZWYucGxvdHNbWzFdXQpgYGAKCiMjIyBTaXRlCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9CnJhbmVmLnBsb3RzW1syXV0KYGBgCgojIyMgU3BlY2llcwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zfQpyYW5lZi5wbG90c1tbM11dCmBgYAoKKioqCgojIFBydW5pbmcgdGhlIG1vZGVsCgotIHJlbW92ZSBgdHJpYWxgIHJhbmRvbSBzbG9wZXMgd2l0aGluIGBzcGVjaWVzYCBhcyB0aGUgZXN0aW1hdGVzIGluIHRoZSBwcmV2aW91cyBtb2RlbHMgd2VyZSBlc3NlbnRpYWxseSAwCgpgYGAKY29ycmVjdCB+IGRlbGF5ICogbm9ybV9hZ2UgKyAKICAgICAgICAgIHRhc2tfZXhwZXJpZW5jZSArIGN1cF9kaXN0YW5jZSArIGJvYXJkX3NpemUgKyB0cmlhbCArCiAgICAgICAgICAoMSArIGRlbGF5ICsgdHJpYWwgfCBzaXRlL3N1YmplY3Rfc2l0ZSApICsgICAgICAgICAKICAgICAgICAgICgxICsgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgYm9hcmRfc2l6ZSArIGRlbGF5IHwgc3BlY2llcykKYGBgCgojIyBDaGVjayBjb2xpbmVhcml0eSBpbiB0aGUgcHJldmlvdXMgbW9kZWwKCmBgYHtyfQpjb2wubW0xIDwtIGdsbShjb3JyZWN0IH4gZGVsYXkgKyBub3JtX2FnZSArCiAgICAgICAgICAgICAgICAgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgYm9hcmRfc2l6ZSArIHRyaWFsCiAgICAgICAgICAgICAgICwgZGF0YSA9IG1vZGVsLmRhdGEKICAgICAgICAgICAgICAgLCBmYW1pbHkgPSBiaW5vbWlhbCkKCnZpZihjb2wubW0xKQpgYGAKCk5vIHNpZ25zIG9mIGhpZ2ggY29saW5hZWFyaXR5LgoKIyMgQ2hlY2sgbGV2ZWxzIG9mIHJhbmRvbSBlZmZlY3RzCgpDaGVjayBob3cgbWFueSBkaWZmZXJlbnQgbGV2ZWxzIHRoZXJlIGFyZSB3aXRoaW4gZWFjaCByYW5kb20gZWZmZWN0CgpgYGB7cn0Kc291cmNlKCJkaWFnbm9zdGljX2ZjbnMuciIpCgpvdmVydmlldyA8LSBmZS5yZS50YWIoImNvcnJlY3QgfiBkZWxheSArIHRhc2tfZXhwZXJpZW5jZSArIGJvYXJkX3NpemUgKyBjdXBfZGlzdGFuY2UgKyB0cmlhbCIsICJzcGVjaWVzIiwgZGF0YSA9IG1vZGVsLmRhdGEpCgpvdmVydmlldyRzdW1tYXJ5CmBgYAoKVGhpcyBzdWdnZXN0cyB0aGF0LCB3aXRoaW4gc3BlY2llcywgcmFuZG9tIHNsb3BlcyBmb3IgYHRhc2tfZXhwZXJpZW5jZWAgZG9lcyBub3QgbWFrZSBtdWNoIHNlbnNlIGFzIG1vc3Qgc3BlY2llcyBoYXZlIG9ubHkgMSBsZXZlbC4gU2FtZSBpcyB0cnVlIGZvciBgY3VwX2Rpc3RhbmNlYCBhbmQgYGJvYXJkX3NpemVgLiBJbmRlZWQsIHRoZSBtb2RlbCBzdW1tYXJ5IGFuZCByYW5kb20gZWZmZWN0cyBwbG90IGZvciBgc3BlY2llc2AgY29uZmlybSB0aGF0IHRoZXJlIGlzIGxpdHRsZSB2YXJpYWJpbGl0eSBpbiB0aGVzZSBlc3RpbWF0ZXMgKHRoZXkncmUgY2xvc2UgdG8gemVybykuIFRoZXJlZm9yZSB0aGV5IGFyZSByZW1vdmVkLgoKYGBgCmNvcnJlY3QgfiBkZWxheSAqIG5vcm1fYWdlICsgCiAgICAgICAgICB0YXNrX2V4cGVyaWVuY2UgKyBjdXBfZGlzdGFuY2UgKyBib2FyZF9zaXplICsgdHJpYWwgKwogICAgICAgICAgKDEgKyBkZWxheSArIHRyaWFsIHwgc2l0ZS9zdWJqZWN0X3NpdGUgKSArICAgICAgICAgCiAgICAgICAgICAoMSArIGRlbGF5IHwgc3BlY2llcykKYGBgCgojIE1vZGVsIDIKClRoZSBtb2RlbCB0YWtlcyBhIHdoaWxlIHRvIHJ1bi4gUnVuIG5leHQgbGluZSB0byBsb2FkIG1vZGVsIG91dHB1dCBmcm9tIHByZXZpb3VzIHJ1biB3aXRoIHN0cnVjdHVyZSBiZWxvdy4KCmBgYHtyfQptbS4yIDwtIHJlYWRSRFMoIjA2XzJfbXBfbW9kZWwyX3YyLnJkcyIpCiMgbW0uMi5jaSA8LSByZWFkUkRTKCIwNl8yX21wX21vZGVsMl9jaV92Mi5yZHMiKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQptbS4yIDwtIGdsbWVyKGNvcnJlY3QgfiBkZWxheSAqIG5vcm1fYWdlICsKICAgICAgICAgICAgICB0YXNrX2V4cGVyaWVuY2UgKyBjdXBfZGlzdGFuY2UgKyBib2FyZF9zaXplICsgdHJpYWwgKwogICAgICAgICAgICAgICgxICsgdHJpYWwgKyBkZWxheSB8IHNpdGUvc3ViamVjdF9zaXRlKSArCiAgICAgICAgICAgICAgKDEgKyBkZWxheSB8IHNwZWNpZXMpCiAgICAgICAgICAgICAgLCBkYXRhID0gbW9kZWwuZGF0YQogICAgICAgICAgICAgICwgZmFtaWx5ID0gYmlub21pYWwKICAgICAgICAgICAgICAsIGNvbnRyb2wgPSBnbG1lckNvbnRyb2wob3B0aW1pemVyID0gImJvYnlxYSIsIG9wdEN0cmwgPSBsaXN0KG1heGZ1biA9IDJlNSkpCiAgICAgICAgICAgICAgKQoKc2F2ZVJEUyhtbS4yLCAiMDZfMl9tcF9tb2RlbDJfdjIucmRzIikKYGBgCgojIE1vZGVsIDMKClJlbW92ZSBgdHJpYWxgIGZyb20gdGhlIHJhbmRvbSBzbG9wZXMgZm9yIHN1YmplY3Qvc2l0ZSBhcyBpdCdzIG5lYXIgemVybyBib3RoIGluIGBtbS4xYCBhbmQgZXZlbiBtb3JlIHNvIGluIGBtbS4yYAoKYGBge3J9ClZhckNvcnIobW0uMikgJT4lIHByaW50KGNvbXAgPSBjKCJWYXJpYW5jZSIsICJTdGQuRGV2LiIpLCBmb3JtYXR0ZXIgPSBmbXQsIGRpZ2l0cyA9IDMpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQpwbG90X21vZGVsKG1tLjIsIHR5cGUgPSAicmUiLCBzb3J0LmVzdCA9ICIoSW50ZXJjZXB0KSIpW1sxXV0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTN9CnBsb3RfbW9kZWwobW0uMiwgdHlwZSA9ICJyZSIsIHNvcnQuZXN0ID0gIihJbnRlcmNlcHQpIilbWzJdXQpgYGAKClRoZSBtb2RlbCB0YWtlcyBhIHdoaWxlIHRvIHJ1bi4gUnVuIG5leHQgbGluZSB0byBsb2FkIG1vZGVsIG91dHB1dCBmcm9tIHByZXZpb3VzIHJ1biB3aXRoIHN0cnVjdHVyZSBiZWxvdy4KCmBgYHtyfQptbS4zIDwtIHJlYWRSRFMoIjA2XzNfbXBfbW9kZWwzX3YyLnJkcyIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9Cm1tLjMgPC0gZ2xtZXIoY29ycmVjdCB+IGRlbGF5ICogbm9ybV9hZ2UgKwogICAgICAgICAgICAgIHRhc2tfZXhwZXJpZW5jZSArIGN1cF9kaXN0YW5jZSArIGJvYXJkX3NpemUgICsgdHJpYWwgKwogICAgICAgICAgICAgICgxICsgZGVsYXkgfCBzaXRlL3N1YmplY3Rfc2l0ZSkgKwogICAgICAgICAgICAgICgxICsgZGVsYXkgfCBzcGVjaWVzKQogICAgICAgICAgICAgICwgZGF0YSA9IG1vZGVsLmRhdGEKICAgICAgICAgICAgICAsIGZhbWlseSA9IGJpbm9taWFsCiAgICAgICAgICAgICAgLCBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiLCBvcHRDdHJsID0gbGlzdChtYXhmdW4gPSAyZTUpKQogICAgICAgICAgICAgICkKCnNhdmVSRFMobW0uMywgIjA2XzNfbXBfbW9kZWwzX3YyLnJkcyIpCmBgYAoKIyMgTW9kZWwgc3VtbWFyeQoKQ29uZmlybSBtb2RlbCBzdHJ1Y3R1cmUKCmBgYHtyfQpmb3JtdWxhKG1tLjMpCmBgYAoKYGBge3IsIHJlc3VsdHM9ImFzaXMifQpnbGFuY2UobW0uMykgJT4lIGthYmxlKGRpZ2l0cyA9IDIpCmBgYAoKIyMgTFJUCgpgYGB7ciwgZXZhbD1GQUxTRX0KZHJvcDEobW0uMywgdGVzdCA9ICdDaGlzcScpCmBgYAoKYGBgClNpbmdsZSB0ZXJtIGRlbGV0aW9ucwoKTW9kZWw6CmNvcnJlY3QgfiBkZWxheSAqIG5vcm1fYWdlICsgdGFza19leHBlcmllbmNlICsgY3VwX2Rpc3RhbmNlICsgCiAgICB0cmlhbCArICgxICsgZGVsYXkgfCBzaXRlL3N1YmplY3Rfc2l0ZSkgKyAoMSArIGRlbGF5IHwgc3BlY2llcykKICAgICAgICAgICAgICAgIERmICAgIEFJQyAgICAgTFJUICBQcihDaGkpICAgIAo8bm9uZT4gICAgICAgICAgICAgNjgzNi4yICAgICAgICAgICAgICAgICAgICAgCnRhc2tfZXhwZXJpZW5jZSAgMSA2ODM0LjQgIDAuMTQ4MyAgIDAuNzAwMSAgICAKY3VwX2Rpc3RhbmNlICAgICAxIDY4NTQuNSAyMC4zMDg3IDYuNTllLTA2ICoqKgp0cmlhbCAgICAgICAgICAgIDEgNjgzNS4wICAwLjc1NDEgICAwLjM4NTIgICAgCmRlbGF5Om5vcm1fYWdlICAgMiA2ODMyLjYgIDAuMzgzNiAgIDAuODI1NSAgICAKLS0tClNpZ25pZi4gY29kZXM6ICAwIOKAmCoqKuKAmSAwLjAwMSDigJgqKuKAmSAwLjAxIOKAmCrigJkgMC4wNSDigJgu4oCZIDAuMSDigJgg4oCZIDEKYGBgCgoKIyMgUmFuZG9tIGVmZmVjdHMKCmBgYHtyfQpWYXJDb3JyKG1tLjMpICU+JSBwcmludChjb21wID0gYygiVmFyaWFuY2UiLCAiU3RkLkRldi4iKSwgZm9ybWF0dGVyID0gZm10LCBkaWdpdHMgPSAzKQpgYGAKCiMjIEZpeGVkIGVmZmVjdHMKCkNJcwoKPCEtLSBCb290c3RyYXAgZnVuY3Rpb24gYnkgUm9nZXIgTXVuZHJ5IEAgTVBJIEVWQSAtLT4KCmBgYHtyLCBldmFsPUZBTFNFfQojIHRoaXMgaXMgbm90IGN1cnJlbnRseSBydW4Kc291cmNlKCJib290X2dsbW0uciIpCgptbS4zLmNpIDwtIGJvb3QuZ2xtbS5wcmVkKG1vZGVsLnJlcyA9IG1tLjMsIGV4Y2wud2FybmluZ3MgPSBGLCBuYm9vdHMgPSAxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgcGFyYSA9IEYsIHJlc29sID0gMTAwLCBsZXZlbCA9IDAuOTUsIHVzZSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICBjaXJjLnZhci5uYW1lID0gTlVMTCwgY2lyYy52YXIgPSBOVUxMLCB1c2UudSA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICBuLmNvcmVzID0gYygiYWxsLTEiLCAiYWxsIiksIHNhdmUucGF0aCA9IE5VTEwpCgpzYXZlUkRTKG1tLjMuY2ksICIwNl8zX21wX21vZGVsM19jaV92Mi5yZHMiKQpgYGAKCmBgYHtyfQptbS4zLmNpIDwtIGNvbmZpbnQobW0uMywgbWV0aG9kID0gIldhbGQiKSAlPiUgIyBib290c3RyYXAgdGhlc2UgbGF0ZXIKICBhcy5kYXRhLmZyYW1lICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbiAlPiUKICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpICU+JQogIHJlbmFtZShMTCA9IGAyLjUgJWAsIFVMID0gYDk3LjUgJWApICU+JQogIG11dGF0ZShPUl9MTCA9IGV4cChMTCksIE9SX1VMID0gZXhwKFVMKSkKYGBgCgpgYGB7ciwgcmVzdWx0cz0iYXNpcyJ9CmNvZWYoc3VtbWFyeShtbS4zKSkgJT4lCiAgYXMuZGF0YS5mcmFtZSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBtdXRhdGUoT1IgPSBleHAoRXN0aW1hdGUpKSAlPiUKICBsZWZ0X2pvaW4obW0uMy5jaSwgYnkgPSAicm93bmFtZSIpICU+JQogIHNlbGVjdChyb3duYW1lLCBPUiwgT1JfTEwsIE9SX1VMLCBFc3RpbWF0ZSwgTEwsIFVMLCBldmVyeXRoaW5nKCkpICU+JQogIGthYmxlKGRpZ2l0cyA9IDMpCmBgYAoKIyMgUGFpcndpc2UgY29udHJhc3RzIGZvciBkZWxheQoKYmFzZWQgb24gZXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KKGVtbSA8LSBlbW1lYW5zKG1tLjMsIHBhaXJ3aXNlIH4gZGVsYXkpJGNvbnRyYXN0cykKYGBgCgpgYGB7cn0KY29uZmludChlbW0pCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CihlbW0ub3IgPC0gZW1tZWFucyhtbS4zLCBwYWlyd2lzZSB+IGRlbGF5LCB0eXBlID0gInJlc3BvbnNlIikkY29udHJhc3RzKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpjb25maW50KGVtbS5vcikKYGBgCgoKIyBNb2RlbCAzIHBsb3RzCgojIyBGaXhlZCBlZmZlY3RzCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9Mi41LCBtZXNzYWdlPUZBTFNFfQpmZS4zIDwtIHBsb3RfbW9kZWwobW0uMywgdGl0bGUgPSAiQS4gRml4ZWQgRWZmZWN0cyIsIGF4aXMubGFiZWxzID0gYygiVHJpYWwiLCAiVGFzayBFeHBlcmllbmNlICh5ZXMpIiwiQm9hcmQgU2l6ZSAoY20pIiwgIkN1cCBEaXN0YW5jZSAoY20pIiwgIk5vcm1lZCBBZ2UgeCBEZWxheVxuKHNob3J0IHZzLiBtZWRpdW0pIiwgIk5vcm1lZCBBZ2UgeCBEZWxheVxuKHNob3J0IHZzLiBsb25nKSIsICJOb3JtZWQgQWdlIiwgIkRlbGF5IChzaG9ydCB2cy4gbG9uZykiLCAiRGVsYXkgKHNob3J0IHZzLiBtZWRpdW0pIiksIHdyYXAubGFiZWxzID0gRiwKICAgICAgICAgICAgICAgICAgb3JkZXIudGVybXMgPSBjKDI6MSwzLDg6OSw1LDYsNCwgNyksCiAgICAgICAgICAgd2lkdGggPSAuMywgc2hvdy52YWx1ZXMgPSBULCB2YWx1ZS5zaXplID0gMi41LCB2YWx1ZS5vZmZzZXQgPSAuMykgKwogIGZhY2V0X3dyYXAofiAiIikgKwogICMgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gMikgKwogICMgYW5ub3RhdGUoImxhYmVsIiwgbGFiZWwgPSAiQSIsIHggPSA4LCB5ID0gLjExLCBzaXplID0gNSkgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2ciLCBsaW1pdHMgPSBjKC4xLCA1LjUpLCBicmVha3MgPSBjKC4xLCAuMiwgLjUsIDEsIDIsIDUpKQoKZmUuMyArIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKC41LCA3LCAuNSwgLjUpLCAiY20iKSkKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3NhdmUoIi4uL2dyYXBocy8wNV9mb3Jlc3RwbG90LnBuZyIsIGZlLjMsIHdpZHRoID0gMywgaGVpZ2h0ID0gMi41LCBzY2FsZSA9IDIpCmBgYAoKIyMgUmFuZG9tIGVmZmVjdHMKCmBgYHtyfQpyYW5lZi5wbG90czIgPC0gcGxvdF9tb2RlbChtbS4zLCB0eXBlID0gInJlIiwgc29ydC5lc3QgPSAiKEludGVyY2VwdCkiKQpgYGAKCiMjIyBTdWJqZWN0L1NpdGUKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KcmFuZWYucGxvdHMyW1sxXV0KYGBgCgojIyMgU2l0ZQoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zfQpyYW5lZi5wbG90czJbWzJdXQpgYGAKCiMjIyBTcGVjaWVzCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9Mn0KcmFuZWYucGxvdHMyW1szXV0KYGBgCgojIE1vZGVsIDQKCi0gZnVydGhlciByZW1vdmUgc3ViamVjdC9zaXRlIHJhbmRvbSBlZmZlY3RzIHRvIGxvb2sgYXQgc3BlY2llcyBkaWZmZXJlbmNlcyAoZnJvbSBwcmVyZWdpc3RyYXRpb24pCgpgYGB7cn0KbW0uNCA8LSBnbG1lcihjb3JyZWN0IH4gZGVsYXkgKiBub3JtX2FnZSArCiAgICAgICAgICAgICAgICB0YXNrX2V4cGVyaWVuY2UgKyBjdXBfZGlzdGFuY2UgKyB0cmlhbCArCiAgICAgICAgICAgICAgICAoMSArIGRlbGF5IHwgc3BlY2llcykKICAgICAgICAgICAgICAsIGRhdGEgPSBtb2RlbC5kYXRhCiAgICAgICAgICAgICAgLCBmYW1pbHkgPSBiaW5vbWlhbAogICAgICAgICAgICAgICwgY29udHJvbCA9IGdsbWVyQ29udHJvbChvcHRpbWl6ZXIgPSAiYm9ieXFhIiwgb3B0Q3RybCA9IGxpc3QobWF4ZnVuID0gMmU1KSkKICAgICAgICApCmBgYAoKIyMgUmFuZG9tIGVmZmVjdHMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpwaHlsbyA8LSByZWFkX2NzdigiLi4vZGF0YS9zcGVjaWVzX2RhdGEuY3N2IikgJT4lIAogIHNlbGVjdChzcGVjaWVzLCBzcGVjaWVzX2Zvcm1hdHRlZCwgcGh5bG8pCmBgYAoKYGBge3J9CnBsb3QuZGF0YSA8LSBnZXRfbW9kZWxfZGF0YShtbS40LCB0eXBlID0gInJlIiwgc2hvdy52YWx1ZXMgPSBUKSAlPiUKICBsZWZ0X2pvaW4ocGh5bG8sIGJ5ID0gYygidGVybSIgPSAic3BlY2llcyIpKSAlPiUKICByZW5hbWUoc3BlY2llcyA9IHNwZWNpZXNfZm9ybWF0dGVkKSAlPiUKICBtdXRhdGUoCiAgICBmYWNldCA9IGZjdF9yZXYoZmFjZXQpLAogICAgZmFjZXQgPSBmY3RfcmVjb2RlKGZhY2V0LCAiSW50ZXJjZXB0IiA9ICJzcGVjaWVzIChJbnRlcmNlcHQpIiwKICAgICAgICAgICAgICAgICAgICAgICAiRGVsYXkgKHNob3J0IHZzLiBsb25nKSIgPSAiZGVsYXlsb25nIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIkRlbGF5IChzaG9ydCB2cy4gbWVkaXVtKSIgPSAiZGVsYXltZWRpdW0iKQogICAgKQoKc29ydGVkIDwtIGZpbHRlcihwbG90LmRhdGEsIGZhY2V0ID09ICJJbnRlcmNlcHQiKSAlPiUgYXJyYW5nZShlc3RpbWF0ZSkgJT4lIHdpdGgoc3BlY2llcykKcGxvdC5kYXRhIDwtIG11dGF0ZShwbG90LmRhdGEsIHNwZWNpZXMgPSBmYWN0b3Ioc3BlY2llcywgbGV2ZWxzID0gc29ydGVkKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Mi41fQpyZS40IDwtIGdncGxvdChwbG90LmRhdGEsIGFlcyh4ID0gc3BlY2llcywgeSA9IGVzdGltYXRlLCBjb2wgPSBncm91cCkpICsKICBmYWNldF9ncmlkKH4gZmFjZXQpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBjb2wgPSAiZ3JleTkwIiwgc2l6ZSA9IDEuMTI1KSArCiAgIyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsdHkgPSAyKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoKSwgd2lkdGggPSAuMykgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIuNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwLmxhYmVsKSwgbnVkZ2VfeCA9IC4zLCBzaXplID0gMi41LCBzaG93LmxlZ2VuZCA9IEYpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoIk9kZHMgUmF0aW9zIiwgdHJhbnMgPSAibG9nIiwgYnJlYWtzID0gYyguMSwgLjIsIC41LCAxLCAyLCA1LDEwLDIwLDQwKSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoLjEsIDQyLjUpKSArIHhsYWIoIiIpICsKICBndWlkZXMoY29sID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgiQi4gU3BlY2llcyBSYW5kb20gRWZmZWN0cyIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTN9Cm1hdCA8LSBtYXRyaXgoYyhyZXAoMSwgMyksIHJlcCgyLCA3KSksIG5yb3cgPSAxKQpncmlkLmFycmFuZ2UoZmUuMywgcmUuNCwgbGF5b3V0X21hdHJpeCA9IG1hdCkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZ2dzYXZlKCIuLi9ncmFwaHMvMDVfZm9yZXN0cGxvdF9mZV9yZS5wbmciLCBhcnJhbmdlR3JvYihmZS4zLCByZS40LCBsYXlvdXRfbWF0cml4ID0gbWF0KSwgd2lkdGggPSA4LCBoZWlnaHQgPSAzLCBzY2FsZSA9IDEuOCkKCmdnc2F2ZSgiLi4vZ3JhcGhzL0ZpZzMudGlmZiIsIGFycmFuZ2VHcm9iKGZlLjMsIHJlLjQsIGxheW91dF9tYXRyaXggPSBtYXQpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDMsIHNjYWxlID0gMS44LCB0eXBlID0gImNhaXJvIiwgY29tcHJlc3Npb24gPSAibHp3IikKYGBgCgoKIyBNb2RlbCBjb21wYXJpc29uCgpXZSdyZSBsb29raW5nIGZvciB0aGUgbG93ZXN0IEFJQyhjKSBhcyB0aGUgbW9kZWwgd2l0aCB0aGUgJ2Jlc3QgZml0JyB3aXRoIGEgcmVhc29uYWJsZSBudW1iZXIgb2YgcGFyYW1ldGVycy4gKFRvbyBtYW55IGFyZSBwZW5hbGl6ZWQgYnkgQUlDIGFzIG9uZSB3YXkgdG8gYWRkcmVzcyBvdmVyZml0dGluZy4pCgpJbmRlZWQsIHRoZSByZWR1Y2VkIG1vZGVsIHNlZW1zIHRvIGRvIGEgYmV0dGVyIGpvYiBvZiBzdHJpa2luZyB0aGF0IGJhbGFuY2UgYmV0d2VlbiBmaXR0aW5nIHRoZSBkYXRhIHdpdGggZmV3ZXIgcGFyYW1ldGVycy4KCmBgYHtyfQpBSUNjdGFiKG1tLjEsIG1tLjIsIG1tLjMsIG1tLjQsIGxvZ0xpayA9IFQsIHdlaWdodHMgPSBUKQpgYGAKCmBgYHtyfQphbm92YShtbS4xLCBtbS4yLCBtbS4zLCBtbS40KQpgYGAKCiMjIERpZmZlcmVuY2UgaW4gcmVncmVzc2lvbiBjb2VmZmljaWVudHMKCkRpZmZlcmVuY2UKCmBgYHtyfQpjb2VmMSA8LSBjb2VmKHN1bW1hcnkobW0uMSkpW2MoMiwgMywgNiksIDFdCmNvZWYyIDwtIGNvZWYoc3VtbWFyeShtbS4zKSlbYygyLCAzLCA2KSwgMV0KCmNvZWYyIC0gY29lZjEKYGBgCgpEaWZmZXJlbmNlIGluIG9kZHMgcmF0aW9zCgpgYGB7cn0KZXhwKGNvZWYyKSAtIGV4cChjb2VmMSkKYGBgCgo=